const ROWS = 4;
const NUMBERS = [2,4];
const MIN_LENGTH = 10;
const MOVE_DURATION = 0.05;

cc.Class({
    extends: cc.Component,

    properties: {
        scoreLable: cc.Label,
        score: 0,
        blockPrefab: cc.Prefab,
        gap: 10,
        bg: cc.Node,
        gameOverBg: cc.Node,
        aginBtn: cc.Node
    },

    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    start() {
        this.drawBlock();
        this.init();
    },
    //绘制方块
    drawBlock() {
        this.blockSize = (cc.winSize.width - (this.gap * (ROWS + 1))) / ROWS;
        let x = this.blockSize / 2 + this.gap;
        let y = this.blockSize + this.gap;
        this.positions = [];

        for (var i = 0; i < ROWS; i++) {
            this.positions.push([0, 0, 0, 0]);
            for (var j = 0; j < ROWS; j++) {
                let block = cc.instantiate(this.blockPrefab)
                block.width = this.blockSize;
                block.height = this.blockSize;
                this.bg.addChild(block);
                //储存每个块的坐标
                this.positions[i][j] = cc.v2(x, y);
                block.setPosition(cc.v2(x, y));
                block.getComponent('block').setNumber(0);
                x += this.blockSize + this.gap;
            }
            x = this.blockSize / 2 + this.gap;
            y += this.blockSize + this.gap;
        }
    },
    init() {
        this.updateScore(0);

        //重置块数组和分数组
        if(this.blocks){
            for(let i=0; i<ROWS; i++){
                for(let j=0; j<ROWS; j++){
                    if(this.blocks[i][j]) this.blocks[i][j].destroy();
                }
            }
        }

        this.blocks = [];
        this.data = [];
        for (var i = 0; i < ROWS; i++) {
            let xArr = [], xNum = [];
            for (var j = 0; j < ROWS; j++) {
                xArr.push(null);
                xNum.push(0);
            }
            this.blocks.push(xArr);
            this.data.push(xNum);
        }

        //添加新的块
        this.addBlock(3);
        //监听拖拽
        this.addEventHandler();
    },
    //更新分数
    updateScore(number) {
        this.scoreLable.string = "分数：" + number;
        this.score = number;
    },
    /**
     * 找出空闲块
     * return 带有位置xy坐标的空闲块数组
     */
    getEnptyBlock() {
        let emptyPos = [];
        for (let row = 0; row < ROWS; row++) {
            for (let col = 0; col < ROWS; col++) {
                if (this.blocks[row][col] == null) {
                    emptyPos.push({ 'x': row, 'y': col });
                }
            }
        }
        return emptyPos;
    },
    /**
     * 添加块
     * @param {添加块的数量} n 
     */
    addBlock(n) {
        for (let i = 0; i < n; i++) {
            let locations = this.getEnptyBlock();
            if (locations.length == 0) return false;
            let location = locations[Math.floor(Math.random() * locations.length)];
            let row = location.x;
            let col = location.y;
            //实例化预制体
            let block = cc.instantiate(this.blockPrefab)
            block.width = this.blockSize;
            block.height = this.blockSize;
            this.bg.addChild(block)
            block.setPosition(this.positions[row][col]);
            let number = NUMBERS[Math.floor(Math.random() * NUMBERS.length)];
            block.getComponent('block').setNumber(number);
            //记录本次出现的块和数字
            this.blocks[row][col] = block;
            this.data[row][col] = number;
        }
    },
    addEventHandler() {
        this.bg.on("touchstart", (event) => {
            this.startPoint = event.getLocation();
        });
        this.bg.on("touchend", (event) => {
            this.touchEnd(event);
        });
        this.bg.on("touchcancel", (event)=>{
            this.touchEnd(event);
        })
    },
    touchEnd(event) {
        this.endPoint = event.getLocation();
        var offset = this.endPoint.sub(this.startPoint);
        if (Math.abs(offset.mag()) > 0) {
            if (Math.abs(offset.x) > Math.abs(offset.y)) {
                //横向
                if (offset.x > 0) {
                    this.moveRight();
                } else {
                    this.moveLeft();
                }
            } else {
                //垂直
                if (offset.y > 0) {
                    this.moveUp();
                } else {
                    this.moveDown();
                }
            }
        }
    },
    /**
     * 移动完毕后
     */
    afterMove(hasMoved){
        //判断是否需要新增块
        if(hasMoved){
            this.updateScore(this.score+1)
            this.addBlock(1);            
        }
        //判断是否游戏结束
        if(this.checkFail()){
           this.gameOver();     
        }
    },
    /**
     * 游戏结束
     */
    gameOver(){
        this.gameOverBg.zIndex = 1000;
        this.gameOverBg.active = true;
        this.aginBtn.on('touchstart', ()=>{
            this.gameOverBg.active = false;
            this.init();
        });
    },
    
    /**
     * 移动格子操作
     */
    doMove(block, position, callback){
        let action = cc.moveTo(MOVE_DURATION, position);
        let finish = cc.callFunc(()=>{
            callback && callback();
        });
        block.runAction(cc.sequence(action, finish));
    },
    /**
     * 检查游戏结束
     */
    checkFail(){
        for(let i=0; i<ROWS; i++){
            for(let j=0; j<ROWS; j++){
                let n = this.data[i][j];
                if(n == 0) {cc.log('0');return false};
                if(j>0 && this.data[i][j-1] == n) {cc.log('left');return false}; //是否还能往左
                if(j<ROWS-1 && this.data[i][j+1] == n) {cc.log('right');return false}; //是否还能往右
                if(i<ROWS-1 && this.data[i+1][j] == n) {cc.log('up');return false}; //是否还能往上
                if(i>0 && this.data[i-1][j] == n) {cc.log('down');return false}; //是否还能往下
            }
        }
        return true;
    },
    moveLeft(){
        cc.log(' left ');
        let toMove = [];
        let hasMoved = false;

        //获取所有可移动的块，从左往右装块
        for(let i=0; i<ROWS; i++){
            for(let j=0; j<ROWS; j++){
                if(this.data[i][j] != 0){
                    toMove.push({x: i, y:j});
                }
            }
        }

        let move = (x, y , callback) => {
            if(y == 0 || this.data[x][y]==0){//自己已经是最边上了，自己是空的块
                callback && callback();//上报afgerMove
                return;
            }else if(this.data[x][y-1] == this.data[x][y]){//可以与左边合并
                //合并
                cc.log('合并');
                let block = this.blocks[x][y];
                let position = this.positions[x][y];
                this.data[x][y-1] *=  2;
                this.blocks[x][y-1].getComponent('block').setNumber(this.data[x][y-1]);
                this.blocks[x][y] = null;
                this.data[x][y] = 0;
                this.doMove(block, position, ()=>{
                    block.destroy();
                    callback && callback();
                });
                hasMoved = true;
            }else if(this.data[x][y-1] == 0){//左边一格什么都没有
                //移动
                cc.log('移动');
                let block = this.blocks[x][y];
                let position = this.positions[x][y-1];//左边的那个格子
                this.blocks[x][y-1] = block;
                this.data[x][y-1] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, position, ()=>{
                    move(x, y-1, callback);
                });
                hasMoved = true;
            }else{
                callback && callback();//上报afgerMove
                return;
            }
        }

        //移动这些块
        let counter = 0;
        for(let i=0; i<toMove.length; i++){
            move(toMove[i].x, toMove[i].y, ()=>{
                ++counter;
                if(counter == toMove.length){
                    this.afterMove(hasMoved);
                }
            });

        }
    },
    moveRight(){
        cc.log(' right ')

        let toMove = [];
        let hasMoved = false;

        //获取所有可移动的块，从右往左装块
        for(let i=0; i<ROWS; i++){
            for(let j=ROWS-1; j>=0; j--){
                if(this.data[i][j] != 0){
                    toMove.push({x: i, y:j});
                }
            }
        }

        let move = (x, y , callback) => {
            if(y == ROWS-1 || this.data[x][y]==0){//自己已经是最边上了，自己是空的块
                callback && callback();
                return;
            }else if(this.data[x][y+1] == this.data[x][y]){//可以与右边合并
                //合并
                cc.log('合并');
                let block = this.blocks[x][y];
                let position = this.positions[x][y];
                this.data[x][y+1] *=  2;
                this.blocks[x][y+1].getComponent('block').setNumber(this.data[x][y+1]);
                this.blocks[x][y] = null;
                this.data[x][y] = 0;
                this.doMove(block, position, ()=>{
                    block.destroy();
                    callback && callback();
                });
                hasMoved = true;
            }else if(this.data[x][y+1] == 0){//右边一格什么都没有
                //移动
                cc.log('移动');
                let block = this.blocks[x][y];
                let position = this.positions[x][y+1];//右边的那个格子
                this.blocks[x][y+1] = block;
                this.data[x][y+1] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, position, ()=>{
                    move(x, y+1, callback);
                });
                hasMoved = true;
            }else{
                callback && callback();
                return;
            }
        }

        //移动这些块
        let counter = 0;
        for(let i=0; i<toMove.length; i++){
            move(toMove[i].x, toMove[i].y, ()=>{
                ++counter;
                if(counter == toMove.length){
                    this.afterMove(hasMoved);
                }
            });

        }
    },
    moveUp(){
        cc.log(' up ');

        let toMove = [];
        let hasMoved = false;

        //获取所有可移动的块，从上往下装
        for(let i=ROWS-1; i>=0; i--){
            for(let j=0; j<ROWS; j++){
                if(this.data[i][j] != 0){
                    toMove.push({x: i, y:j});
                }
            }
        }

        let move = (x, y , callback) => {
            if(x == ROWS-1 || this.data[x][y]==0){//自己已经是最边上了，自己是空的块
                callback && callback();
                return;
            }else if(this.data[x+1][y] == this.data[x][y]){//可以与上边合并
                //合并
                cc.log('合并');
                let block = this.blocks[x][y];
                let position = this.positions[x][y];
                this.data[x+1][y] *=  2;
                this.blocks[x+1][y].getComponent('block').setNumber(this.data[x+1][y]);
                this.blocks[x][y] = null;
                this.data[x][y] = 0;
                this.doMove(block, position, ()=>{
                    block.destroy();
                    callback && callback();
                });
                hasMoved = true;
            }else if(this.data[x+1][y] == 0){//上边一格什么都没有
                //移动
                cc.log('移动');
                let block = this.blocks[x][y];
                let position = this.positions[x+1][y];//上边的那个格子
                this.blocks[x+1][y] = block;
                this.data[x+1][y] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, position, ()=>{
                    move(x+1, y, callback);
                });
                hasMoved = true;
            }else{
                callback && callback();
                return;
            }
        }

        //移动这些块
        let counter = 0;
        for(let i=0; i<toMove.length; i++){
            move(toMove[i].x, toMove[i].y, ()=>{
                ++counter;
                if(counter == toMove.length){
                    this.afterMove(hasMoved);
                }
            });

        }
    },
    moveDown(){
        cc.log(' down ');

        let toMove = [];
        let hasMoved = false;

        //获取所有可移动的块，从下往上装
        for(let i=0; i<ROWS; i++){
            for(let j=ROWS-1; j>=0; j--){
                if(this.data[i][j] != 0){
                    toMove.push({x: i, y:j});
                }
            }
        }

        let move = (x, y , callback) => {
            if(x == 0 || this.data[x][y]==0){//自己已经是最边下了，自己是空的块
                callback && callback();
                return;
            }else if(this.data[x-1][y] == this.data[x][y]){//可以与下边合并
                //合并
                cc.log('合并');
                let block = this.blocks[x][y];
                let position = this.positions[x][y];
                this.data[x-1][y] *=  2;
                this.blocks[x-1][y].getComponent('block').setNumber(this.data[x-1][y]);
                this.blocks[x][y] = null;
                this.data[x][y] = 0;
                this.doMove(block, position, ()=>{
                    block.destroy();
                    callback && callback();
                });
                hasMoved = true;
            }else if(this.data[x-1][y] == 0){//下边一格什么都没有
                //移动
                cc.log('移动');
                let block = this.blocks[x][y];
                let position = this.positions[x-1][y];//下边的那个格子
                this.blocks[x-1][y] = block;
                this.data[x-1][y] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, position, ()=>{
                    move(x-1, y, callback);
                });
                hasMoved = true;
            }else{
                callback && callback();
                return;
            }
        }

        //移动这些块
        let counter = 0;
        for(let i=0; i<toMove.length; i++){
            move(toMove[i].x, toMove[i].y, ()=>{
                ++counter;
                if(counter == toMove.length){
                    this.afterMove(hasMoved);
                }
            });

        }
    }

});